const { v4 } = require("uuid");
const path = require('path');
const db = require("@/db/mysql");
const redisClient = require("@/redis/redis-client");
const redisKeysConstant = require("@/constants/redis-keys.constant");
const AesSecurity = require("@/security/aes.security");
const CommonService = require("./common.service");
const AppConfig = require("$config/app.config");
const aesSecurity = new AesSecurity();
const { TenantContext } = require("@/core/tenant-context");

const time = () => {
	const now = new Date();
	const date = [now.getFullYear(), now.getMonth() + 1, now.getDate()].map(i => (i < 10 ? "0" + i : i)).join("-");
	const time = [now.getHours(), now.getMinutes(), now.getSeconds()].map(i => (i < 10 ? "0" + i : i)).join(":");
	return `${date} ${time}`;
};

/**
 * 业务文件服务类
 * @author Haor
 * @create_time 2024/02/14
 */
class FileBusinessService {
    commonService = new CommonService();
	/**
	 * 绑定业务
	 * @param {*} param0
	 * @returns
	 */
	async bidBizFile({ id, file_id, business_id, file_category, sort_order, is_cover, extra_data }) {
		try {
			const { code } = await db.query(`
            INSERT INTO file_bindings (
                id, tenant_id, file_id, business_id, file_category, sort_order, is_cover, extra_data, created_by, created_at
            )
            VALUES
                (
                '${id}', '${TenantContext.getCurrentTenant()}', '${file_id}', '${business_id}', '${file_category}', '${sort_order}', ${is_cover}, '${extra_data}', '${TenantContext.getCurrentUser()}', '${time()}'
                )
            `);
			const isSuccess = code === 200;
            if (isSuccess) {
                await this.commonService.setRowLocked('file_files', file_id, 'id');
            }
			return [isSuccess ? null : "设置失败"];
		} catch (error) {
			return [error];
		}
	}

    /**
     * 绑定附件
     * @param {*} param0 
     * @returns 
     */
    async bindingFiles({ business_id, files, business_type }) {
        try {
            files = typeof files === 'string' ? [files] : files;
             const setValues = (sql) => {
                business_type = business_type || unknown;
                const values = files.map(file => {
                    const id = v4().toUpperCase();
                    return `('${id}', '${TenantContext.getCurrentTenant()}', '${file}', '${business_type}', '${business_id}', '${TenantContext.getCurrentUser()}', '${time()}')`
                }).join(', ')
                return `${sql} VALUES ${values}`;
            }
            const SQL = setValues(`INSERT INTO file_bindings (id, tenant_id, file_id, business_type, business_id, created_by, created_at)`);
            const result = await db.query(SQL);
            if (result.code !== 200) {
                return [true, '绑定附件失败']; 
            }
           await this.commonService.setRowLocked('file_files', files); // 上锁源数据
           return [null];
       } catch (error) {
           return [true, error.message];
       }
        
    }

    /**
     * 删除业务附件
     * @param {*} param0 
     */
    async deleteBusinessFiles({ business_id, file_ids, is_all }) {
        try {
            // 删除全部
            if (is_all) {
                const SQL = `
                    UPDATE  
                        file_bindings 
                    SET 
                        is_delete = 1 
                    WHERE
                        business_id = '${business_id}' 
                        AND tenant_id = '${TenantContext.getCurrentTenant()}'
                `;
                const res = await db.query(SQL);
                if (res.code === 200) {
                    return true;
                }
                return false;
            }
            // 删除指定的
            if (file_ids && file_ids.length > 0) {
                const ids = file_ids.map(i => `'${i}'`).join(', ')
                const SQL = `UPDATE file_bindings SET is_delete = 1 WHERE file_id IN (${ids}) AND business_id = '${business_id}' AND tenant_id = '${TenantContext.getCurrentTenant()}'`;
                const res = await db.query(SQL)
                if (res.code === 200) {
                    return true;
                }
                return false;
            }
            return false;
        } catch (error) {
            console.log('删除业务文件失败');
            return false;
        }
    }

    /**
     * 更新业务文件
     * @param {*} param0 
     */
    async updateBusinessFiles({ business_id, files, business_type }) {
        try {
            files = files || [];
            // 如果是空，直接清空数组
            if (files.length === 0) {
                const removeResult = await this.deleteBusinessFiles({ business_id: business_id, is_all: true });
                if (removeResult) {
                    return true;
                }
                return false;
            }

            const filesIds = files.map(file => {
                if (typeof file === 'string') return file;
                if (typeof file === 'object' && file !== null) {
                    return file.id;
                }
                return null;
            }).filter(Boolean);
            const originFiles = await this.getBusinessFiles(business_id, false); // 获取原始的文件
            const originIdList = originFiles.map(file => file.id);
            const deleteFileIds = [];
            const newFileIds = [];
            // 新增的文件
            files.forEach(file => {
                if (!originIdList.includes(file)) {
                    newFileIds.push(file)
                }
            });

            // 删除的文件
            originIdList.forEach(id => {
                if (!filesIds.includes(id)) {
                    deleteFileIds.push(id);
                }
            });

            // 新增的文件
            if (newFileIds.length > 0) {
                await this.bindingFiles({ business_id, files: newFileIds, business_type: business_type })
            }

            // 删除的文件
            if (deleteFileIds.length > 0) {
                await this.deleteBusinessFiles({ business_id, file_ids: deleteFileIds });
            }
            return true;
        } catch (error) {
            console.log('修改业务文件失败：', error.message);
            return false;
        }
    }


    /**
     * 设置业务附件列表
     * @param {*} param0 
     * @param {array} files upload上传返回的文件id列表
     * @param {string} biz_id 业务id
     * @param {string} uid 用户id
     * @returns {boolean}
     */
    async bidBizFileList({ files, biz_id, uid, gid }) {
        try {
            const SQL = `
            INSERT INTO file_biz_files (
                id, file_id, biz_id, create_user, create_time
            )
            VALUES 
                ${files.map(id => {
                    return `('${v4()}', '${id}', '${biz_id}', '${uid}', '${time()}')`
                }).join(', ')}
            `;
            const result = await db.query(SQL);
            if (result.code === 200) {
                await this.commonService.setRowLocked('file_files', files, 'id'); // 上传的附件上锁
                await this.syncFamilyGroupStoreSize(gid); // 同步存储大小
                return true;
            }
            return false;
        } catch (error) {
            console.log('设置业务附件列表失败：', error);
            return false;
        }
    }

	/**
	 * 获取业务文件
	 * @param {*} id
	 */
	async getBusinessFiles(id, is_preview) {
		try {
			const SQL = `
                SELECT
                    f.id, f.name, f.path, f.size, fb.id fb_id, 
                    f.suffix, f.original_name, f.created_at 
                FROM 
                    file_bindings fb 
                    LEFT JOIN file_files f ON fb.file_id = f.id 
                WHERE
                    fb.business_id = '${id}' 
                    AND f.is_delete = 0 
                    AND fb.is_delete = 0 
                    AND f.tenant_id = '${TenantContext.getCurrentTenant()}' 
                ORDER BY f.created_at ASC 
                LIMIT 100
            `;
			const { code, results } = await db.query(SQL);
            if (code === 200) {
                if (!is_preview) return results;
                const files = this.setFilesPreviewPath({ files: results });
                return files;
			}
			return [];
        } catch (error) {
            console.log('查询业务文件列表失败：', error);
			return [];
		}
	}

	/**
	 * 查询列表图片列表
	 * @param {*} bizIdList 业务id列表
	 * @returns
	 */
	async getRowsBusinessFiles(bizIdList) {
		try {
			const SQL = `
				SELECT
					bf.business_id, f.id, f.name, f.path, f.size, f.suffix, f.original_name, f.created_by, f.created_at 
				FROM file_bindings bf
					LEFT JOIN file_files f ON bf.file_id = f.id
				WHERE bf.business_id IN (${bizIdList.map(id => "'" + id + "'").join(", ")})
					AND f.is_delete = 0 
                    AND bf.is_delete = 0
				LIMIT 100
			`;
			const { code, results } = await db.query(SQL);
			const result = {};
			if (code === 200) {
				results.forEach(row => {
					if (!result[row.business_id]) {
						result[row.business_id] = [];
					}
					result[row.business_id].push(row);
				});
			}
			return result;
		} catch (err) {
			console.log("error: " + err.message);
			return {};
		}
	}

	/**
	 * 设置业务行的文件
	 * @param {array} rows 列表
	 * @param {string} businessIdFiled 文件业务id key
	 * @param {string} fileKey 最后的附件key
	 * @returns
	 */
	async setBusinessRowsFile({ rows, businessIdFiled, fileKey }) {
		const fileIds = rows.filter(itm => itm[businessIdFiled]).map(i => i[businessIdFiled]); // 文件id列表
		if (fileIds.length > 0) {
			const filesMap = await this.getRowsBusinessFiles(fileIds); // 通过id列表查询所有的文件集合
			for (let i = 0; i < rows.length; i++) {
				const row = rows[i];
				const rowFiles = Array.isArray(filesMap[row[businessIdFiled]]) ? filesMap[row[businessIdFiled]] : null;
				if (rowFiles) {
					await this.setFilesPreviewPath({ files: rowFiles });
				}
				row[fileKey] = rowFiles;
			}
		}
		return rows;
	}

	/**
	 * 设置文件预览地址
	 * @param {*} param0
	 */
	async setFilesPreviewPath({ files, previewkey, downloadKey }) {
		for (let i = 0; i < files.length; i++) {
			const file = files[i];
			const createTime = Date.now();
			const extTime = createTime + AppConfig.FILE_EFFECTIVE_TIME; // 20分钟过期
			const encryptCode = aesSecurity.encrypt({
				fid: file.id,
				path: path.join(AppConfig.FILE_UPLOAD_PATH, file.path),
				n: file.original_name,
				ext: extTime
			});
			const cacheId = v4().replace(/[-_ ]/g, "").toUpperCase(); // 随机id
            file[previewkey || 'preview_path'] = `/file/preview/${file.original_name}?c=${encodeURIComponent(cacheId)}`; // 预览地址
			file[downloadKey || 'download_path'] = `/downloads/${encodeURIComponent(cacheId)}/${file.original_name}`; // 下载地址
			// 将code存储到redis中，用户访问预览地址的时候可以用
			await this._setPreviewKeysToRedis(cacheId, {
				fileId: file.id,
				code: encryptCode,
				create_time: createTime,
				name: file.original_name,
                extTime: extTime,
                // path: file.path
			});
            // delete file.path;
		}
		return files;
	}

    /**
     * 删除主表附件
     * @param {string} fileid 文件id
     */
    async deleteFile(fileid) {
        try {
            const SQL = `UPDATE file_files SET is_delete = 1, update_time = '${this.time}' WHERE id = '${fileid}'`;
            const delResult = await db.query(SQL);
            if (delResult.code === 200 && delResult.results.changeRows > 0) {
                return true;
            }
            return false;
        } catch (error) {
            console.log('删除主表附件失败：', error);
            return false;
        }
    }

    /**
     * 删除业务绑定的附件
     * @param {*} bizId 
     * @returns 
     */
    async _deleteBindFileByBizId(bizId) {
        try {
            const SQL = `DELETE FROM file_biz_files WHERE biz_id = '${bizId}'`;
            const result = await db.query(SQL);
            if (result.code === 200) {
                return true;
            }
            return false;
        } catch (error) {
            console.log('删除业务绑定附件错误：', error);
            return false;
        }
    }

	/**
	 * 设置预览的keys到redis中
	 * @param {*} id
	 * @param {*} data
	 */
	async _setPreviewKeysToRedis(id, data) {
		try {
            console.time('file:preview')
			const key = redisKeysConstant.filePreview(id);
			let keys = (await redisClient.get(redisKeysConstant.filePreviewKeys)) || [];
			keys.push(id);
			await redisClient.set(redisKeysConstant.filePreviewKeys, keys); // 同步到redis中
			await redisClient.set(key, data); // 同步到redis
            console.timeEnd('file:preview')
			return true;
		} catch (error) {
			console.log("设置预览的keys到redis中: error", error);
			return false;
		}
	}

	/**
	 * 删除预览过期的文件
	 * @param {*} key
	 * @returns
	 */
	async deletePreviewExpiredFile(key) {
		try {
			const keys = (await redisClient.get(redisKeysConstant.filePreviewKeys)) || [];
			await redisClient.set(
				redisKeysConstant.filePreviewKeys,
				keys.filter(k => k !== key)
			); // 删除keys中的内容
			await redisClient.del(redisKeysConstant.filePreview(key)); // 删除redis中的内容
			return true;
		} catch (error) {
			return false;
		}
	}

    /**
     * 同步家庭组存储大小
     * @param {*} gid 家庭组id
     * @returns 
     */
    async syncFamilyGroupStoreSize(gid) {
        try {
            const SQL = `SELECT SUM(size) size FROM file_files WHERE family_group_id = '${gid}' AND is_lock = 1 AND is_delete = 0`; // 查询当前分组下的所有附件列表
            const sizeResult = await db.query(SQL);
            if (sizeResult.code === 200 && sizeResult.results.length > 0) {
                const size = sizeResult.results[0].size;
                if (size > 0) {
                    const SET_STORE_SIZE_SQL = `UPDATE family_group SET store_size = ${size}, update_time='${time()}' WHERE id = '${gid}'`;
                    const setResult = await db.query(SET_STORE_SIZE_SQL);
                    if (setResult.code === 200 && setResult.changeRows > 0) {
                        return true;
                    }
                }
            }
            return false;
        } catch (error) {
            console.log('同步家庭组存储大小错误：', error);
            return false;
        }
    }
}

module.exports = new FileBusinessService();
